﻿using LearnMAUI2.Models;
using SQLite;
using System.Linq.Expressions;

namespace LearnMAUI2.Services;

public class PoetryStorage : IPoetryStorage
{
    //使用嵌入式资源
    public const string DbName = "poetrydb.sqlite3";
    public static readonly string PoetryDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DbName);

    private SQLiteAsyncConnection _connection;

    //使用上面的安全数据库文件路径打开数据库
    public SQLiteAsyncConnection Connection => _connection ??= new SQLiteAsyncConnection(PoetryDbPath);

    //同样是拷贝
    //这里就可以看到依赖倒置（依赖接口的好处），因为没办法直接从.NET 8项目中使用MAUI独有的Preferences，但是通过接口就可以
    private readonly IPreferenceStorage _preferenceStorage;

    public PoetryStorage(IPreferenceStorage preferenceStorage)
    {
        _preferenceStorage = preferenceStorage;
    }

    //判断数据库是否初始化
    //存储数据库版本号来区分新旧，注意版本号是键值对类型，因此不适合保存在关系数据库，而适合保存在键值数据库
    //从偏好存储中读出版本，如果没有，默认值是0，表示用户现有的版本
    //看现有的版本和当前代码要求的版本是否相同来判断是否正确初始化
    public bool IsInitialized => _preferenceStorage.Get(PoetryStroageConstant.VersionKey, 0) == PoetryStroageConstant.Version;

    //等价于
    //public bool IsInitialized
    //{
    //    get {
    //        if (Preferences.Get(PoetryStroageConstant.VersionKey, 0) == PoetryStroageConstant.Version)
    //            return true;
    //        return false;
    //    }
    //}

    public async Task<IEnumerable<Poetry>> GetPoetriesAsync(Expression<Func<Poetry, bool>> where, int skip, int take) => await Connection.Table<Poetry>()
        .Where(where)
        .Skip(skip)
        .Take(take)
        .ToListAsync();

    public async Task<Poetry> GetPoetryAsync(int id)=> await Connection.Table<Poetry>().FirstOrDefaultAsync(x => x.Id == id);

    //将嵌入式文件拷贝到用户文件夹
    public async Task InitializeAsync()
    {
        //1、使用定义好的路径打开目标文件，注意文件/数据库用完一定要关闭，或是使用using自动关闭，直接使用最新的using语法极氪
        await using var dbFileString = new FileStream(PoetryDbPath, FileMode.OpenOrCreate);
        //2、打开数据库资源，获得当前项目编译出来的assembly，注意PoetryStorage不重要，可以是该项目中的任何一个.cs       
        //通过这样的方式来找到资源流中的poetrydb.sqlite3，这种写法也是固定写法
        //可以带上一点防御手段，提示空指针错误
        await using var dbAssetStream = typeof(PoetryStorage).Assembly.GetManifestResourceStream(DbName) ?? throw new Exception($"Not found {DbName}");
        //3、把sqlite资源流copy到文件
        await dbAssetStream.CopyToAsync(dbFileString);
        //存版本号
        _preferenceStorage.Set(PoetryStroageConstant.VersionKey, PoetryStroageConstant.Version);
    }

    //关闭数据库，他不是业务功能，所以不需要在接口中定义
    public async Task CloseAsync()=>await Connection.CloseAsync();
}

//存储常量，存储版本号来追踪数据库版本变化，他也是EF的基础原理
public static class PoetryStroageConstant
{
    //如果没有初始化，不会有数据库，自然也没有版本号1
    //代表正确的版本，也就是偏好存储中应该有的版本
    public const int Version = 1;
    //键值对键名称(使用nameof属性，这样就不必担心后续类名变更)
    //代表偏好存储（即键值对存储）中存放的版本，程序第一次运行未初始化前不会有，初始化后才会有
    public const string VersionKey = nameof(PoetryStroageConstant) + "." + nameof(Version);
}